package mm.test;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Map;

import org.apache.jasper.runtime.HttpJspBase;

import mm.util.LOG;

public class JspClassLoader extends ClassLoader {

	private static final JspClassLoader LOADER=new JspClassLoader();
	private static final String ANT_HOME=System.getenv("ANT_HOME");
	private static final String JSP_TMP_PATH="/jspTmp";
	private static final Map<String, Class<HttpJspBase>> CLASS_CACHE=new HashMap<String, Class<HttpJspBase>>();
	
	private JspClassLoader(){
		super();
	}
	
	public static JspClassLoader getInstance(){
		return LOADER;
	}

	@SuppressWarnings("unchecked")
	public HttpJspBase loadJsp(String path) throws ClassNotFoundException {// /aaa/main.jsp
		if(CLASS_CACHE.containsKey(path)){
			Class<HttpJspBase> existingClass = CLASS_CACHE.get(path);
			try {
				return existingClass.newInstance();
			} catch (Exception e) {
				e.printStackTrace();
				throw new ClassNotFoundException();
			}
		}
		String className=path;
		className=path.substring(1);// aaa/main.jsp
		className=className.replaceAll("\\-", "_002d");
		className=className.split("\\.")[0];// aaa/main
		className=className.replace('/', '.');// aaa.main
		className+="_jsp"; // aaa.main_jsp
		String jspPackageName=this.getClass().getPackage().getName();
		jspPackageName=jspPackageName.substring(0, jspPackageName.lastIndexOf(".")+1)+"jsp";
		className=jspPackageName+"."+className; // xxx.jsp.aaa.main_jsp
		try{
			Class<HttpJspBase> clazz = (Class<HttpJspBase>) super.loadClass(className);
			HttpJspBase instance = clazz.newInstance();
			CLASS_CACHE.put(path, clazz);
			return instance;
		}catch(Exception e){
			String classPath=this.getClass().getResource("/"+this.getClass().getName().replace('.', '/')+".class").getPath();
			classPath=classPath.substring(0, classPath.lastIndexOf("/webapp"));
			classPath+=JSP_TMP_PATH+"/"+className.replace('.', '/')+".class";
			File file=new File(classPath);
			if(!file.exists()){
				LOG.debug("Compiling: "+className);
				String buildFilePath=classPath.substring(0, classPath.indexOf(JSP_TMP_PATH))+"/build_jsp_test_compile.xml";
				Runtime rt = Runtime.getRuntime();
				try {
					Process p = rt.exec(ANT_HOME+"\\bin\\ant.bat -f "+buildFilePath+" -Djsp="+path.substring(1));
					java.io.BufferedReader stdInput = new java.io.BufferedReader(
							new java.io.InputStreamReader(p.getInputStream()));
					java.io.BufferedReader stdError = new java.io.BufferedReader(
							new java.io.InputStreamReader(p.getErrorStream()));
					// read the output from the command
					String s = null;
					System.out.println("Here is the standard output of the command:\n");
					while ((s = stdInput.readLine()) != null) {
						System.out.println(s);
					}
					// read any errors from the attempted command
					System.out.println("Here is the standard error of the command (if any):\n");
					while ((s = stdError.readLine()) != null) {
						System.out.println(s);
					}
				} catch (IOException e1) {
					e1.printStackTrace();
					throw new ClassNotFoundException();
				}
				file=new File(classPath);
				if(!file.exists()){
					throw new ClassNotFoundException();
				}
			}
			InputStream is=null;
			try {
				is = new FileInputStream(file);
			} catch (FileNotFoundException e1) {
				e1.printStackTrace();
				throw new ClassNotFoundException();
			}
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			try{
				byte[] buf = new byte[1024];
				while (true) {
					int len = is.read(buf);
					if (len < 0) break;
					baos.write(buf, 0, len);
				}
			}catch(IOException ioe){
				ioe.printStackTrace();
				throw new ClassNotFoundException();
			}
			// Use a protectionDomain to associate the codebase with the
            // class.
            ProtectionDomain pd = this.getClass().getProtectionDomain();
			int i = className.lastIndexOf('.');
			if (i != -1) {
				String pkgname = className.substring(0, i);
				// Check if package already loaded.
				Package pkg = getPackage(pkgname);
				if (pkg != null) {
					// Package found, so check package sealing.
					if (pkg.isSealed()) {
						// Verify that code source URL is the same.
						LOG.error("SecurityException: sealing violation: package " + pkgname + " is sealed");
					}
				} else {
					definePackage(pkgname, null, null, null, null, null, null, null);
				}
			}
			byte[] bytes=baos.toByteArray();
			try{
				Class<HttpJspBase> clazz = (Class<HttpJspBase>) defineClass(className, bytes, 0, bytes.length, pd);
				HttpJspBase instance = clazz.newInstance();
				CLASS_CACHE.put(path, clazz);
	            return instance;
			}catch(Exception e0){
				e0.printStackTrace();
				throw new ClassNotFoundException();
			}
		}
	}

}
